/* // 运行 */
#include <math.h>
#include <string.h>
#include <setjmp.h>
#include "MEM.h"
#include "DBG.h"
#include "voble".h"

static StatementResult
execute_statement(VB_Interpreter *inter, VB_LocalEnvironment *env,
                  Statement *statement);

static StatementResult
execute_expression_statement(VB_Interpreter *inter, VB_LocalEnvironment *env,
                             Statement *statement)
{
    StatementResult result;

    result.type = NORMAL_STATEMENT_RESULT;

    VB_eval_expression(inter, env, statement->u.expression_s);

    return result;
}

static StatementResult
execute_global_statement(VB_Interpreter *inter, VB_LocalEnvironment *env,
                         Statement *statement)
{
    IdentifierList *pos;
    StatementResult result;

    result.type = NORMAL_STATEMENT_RESULT;

    if (env == NULL) {
        vb_runtime_error(statement->line_number,
                          GLOBAL_STATEMENT_IN_TOPLEVEL_ERR,
                          MESSAGE_ARGUMENT_END);
    }
    for (pos = statement->u.global_s.identifier_list; pos; pos = pos->next) {
        GlobalVariableRef *ref_pos;
        GlobalVariableRef *new_ref;
        Variable *variable;
        for (ref_pos = env->global_variable; ref_pos;
             ref_pos = ref_pos->next) {
            if (!strcmp(ref_pos->name, pos->name))
                goto NEXT_IDENTIFIER;
        }
        variable = wj_search_global_variable(inter, pos->name);
        if (variable == NULL) {
            wj_runtime_error(statement->line_number,
                              GLOBAL_VARIABLE_NOT_FOUND_ERR,
                              STRING_MESSAGE_ARGUMENT, "name", pos->name,
                              MESSAGE_ARGUMENT_END);
        }
        new_ref = MEM_malloc(sizeof(GlobalVariableRef));
		new_ref->name = pos->name;
        new_ref->variable = variable;
        new_ref->next = env->global_variable;
        env->global_variable = new_ref;
      NEXT_IDENTIFIER:
        ;
    }

    return result;
}

static StatementResult
execute_elsif(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
              Elsif *elsif_list, WJ_Boolean *executed)
{
    StatementResult result;
    WJ_Value   cond;
    Elsif *pos;

    *executed = WJ_FALSE;
    result.type = NORMAL_STATEMENT_RESULT;
    for (pos = elsif_list; pos; pos = pos->next) {
        cond = wj_eval_expression(inter, env, pos->condition);
        if (cond.type != WJ_BOOLEAN_VALUE) {
            wj_runtime_error(pos->condition->line_number,
                              NOT_BOOLEAN_TYPE_ERR, MESSAGE_ARGUMENT_END);
        }
        if (cond.u.boolean_value) {
            result = wj_execute_statement_list(inter, env,
                                                pos->block->statement_list);
            *executed = WJ_TRUE;
            if (result.type != NORMAL_STATEMENT_RESULT)
                goto FUNC_END;
        }
    }

  FUNC_END:
    return result;
}

static StatementResult
execute_if_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                     Statement *statement)
{
    StatementResult result;
    WJ_Value   cond;

    result.type = NORMAL_STATEMENT_RESULT;
    cond = wj_eval_expression(inter, env, statement->u.if_s.condition);
    if (cond.type != WJ_BOOLEAN_VALUE) {
        wj_runtime_error(statement->u.if_s.condition->line_number,
                          NOT_BOOLEAN_TYPE_ERR, MESSAGE_ARGUMENT_END);
    }
    DBG_assert(cond.type == WJ_BOOLEAN_VALUE, ("cond.type..%d", cond.type));

    if (cond.u.boolean_value) {
        result = wj_execute_statement_list(inter, env,
                                            statement->u.if_s.then_block
                                            ->statement_list);
    } else {
        WJ_Boolean elsif_executed;
        result = execute_elsif(inter, env, statement->u.if_s.elsif_list,
                               &elsif_executed);
        if (result.type != NORMAL_STATEMENT_RESULT)
            goto FUNC_END;
        if (!elsif_executed && statement->u.if_s.else_block) {
            result = wj_execute_statement_list(inter, env,
                                                statement->u.if_s.else_block
                                                ->statement_list);
        }
    }

  FUNC_END:
    return result;
}

static StatementResultType
compare_labels(char *result_label, char *loop_laber,
			StatementResultType current_result)
{
	if (result_label == NULL) {
		return NORMAL_STATEMENT_RESULT;
	}

	if (loop_laber && !strcmp(result_label, loop_laber)) {
		return NORMAL_STATEMENT_RESULT;
	} else {
		return current_result;
	}
}


static StatementResult
execute_while_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                        Statement *statement)
{
    StatementResult result;
    WJ_Value   cond;

    result.type = NORMAL_STATEMENT_RESULT;
    for (;;) {
        cond = wj_eval_expression(inter, env, statement->u.while_s.condition);
        if (cond.type != WJ_BOOLEAN_VALUE) {
            wj_runtime_error(statement->u.while_s.condition->line_number,
                              NOT_BOOLEAN_TYPE_ERR, MESSAGE_ARGUMENT_END);
        }
        DBG_assert(cond.type == WJ_BOOLEAN_VALUE,
                   ("cond.type..%d", cond.type));
        if (!cond.u.boolean_value)
            break;

        result = wj_execute_statement_list(inter, env,
                                            statement->u.while_s.block
                                            ->statement_list);
        if (result.type == RETURN_STATEMENT_RESULT) {
            break;
        } else if (result.type == BREAK_STATEMENT_RESULT) {
            result.type = NORMAL_STATEMENT_RESULT;
            break;
        } else if (result.type == CONTINUE_STATEMENT_RESULT) {
			result.type = NORMAL_STATEMENT_RESULT;
			break;
		}
    }

    return result;
}

static StatementResult
execute_for_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                      Statement *statement)
{
    StatementResult result;
    WJ_Value   cond;

    result.type = NORMAL_STATEMENT_RESULT;

    if (statement->u.for_s.init) {
        wj_eval_expression(inter, env, statement->u.for_s.init);
    }
    for (;;) {
        if (statement->u.for_s.condition) {
            cond = wj_eval_expression(inter, env,
                                       statement->u.for_s.condition);
            if (cond.type != WJ_BOOLEAN_VALUE) {
                wj_runtime_error(statement->u.for_s.condition->line_number,
                                  NOT_BOOLEAN_TYPE_ERR, MESSAGE_ARGUMENT_END);
            }
            DBG_assert(cond.type == WJ_BOOLEAN_VALUE,
                       ("cond.type..%d", cond.type));
            if (!cond.u.boolean_value)
                break;
        }
        result = wj_execute_statement_list(inter, env,
                                            statement->u.for_s.block
                                            ->statement_list);
        if (result.type == RETURN_STATEMENT_RESULT) {
            break;
        } else if (result.type == BREAK_STATEMENT_RESULT) {
			result.type = compare_labels(result.u.label, 
					statement->u.for_s.label, result.type);
            break;
        } else if (result.type == CONTINUE_STATEMENT_RESULT) {
			result.type = compare_labels(result.u.label,
					statement->u.for_s.label, result.type);
		}


        if (statement->u.for_s.post) {
            wj_eval_expression(inter, env, statement->u.for_s.post);
        }
    }

    return result;
}

static WJ_Value *
assign_to_variable(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
					int line_number, char *name, WJ_Value *value)
{
	WJ_Value *ret;

	ret = wj_get_identifier_lvalue(inter, env, line_number, name);
	if (!ret) {
		if (env) {
			ret = WJ_add_local_variable(inter, env, name, value, WJ_FALSE);
		} else {
			/* if (wj_search_function(inter, name)) { */
			if (wj_search_function(name)) {
				wj_runtime_error(line_number, FUNCTION_EXISTS_ERR,
								STRING_MESSAGE_ARGUMENT, "name", name,
								MESSAGE_ARGUMENT_END);
			}
			ret = WJ_add_global_variable(inter, name, value, WJ_FALSE);
		}
	} else {
		*ret = *value;
	}

	return ret;
}

static StatementResult
execute_foreach_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
						Statement *statement)
{
	StatementResult result;
	WJ_Value *collection;
	WJ_Value iterator;
	WJ_Value *var;
	WJ_Value is_done;
	int stack_count = 0;
	WJ_Value temp;

	result.type = NORMAL_STATEMENT_RESULT;

	collection = wj_eval_and_peek_expression(inter, env,
											statement->u.foreach_s.collection);
	stack_count++;
	collection = WJ_peek_stack(inter, 0);

	iterator = WJ_call_method(inter, env, statement->line_number, 
							collection->u.object, ARRAY_METHOD_ITERATOR,
							0, NULL);
	WJ_push_value(inter, &iterator);
	stack_count++;

	temp.type = WJ_NULL_VALUE;
	var = assign_to_variable(inter, env, statement->line_number, 
							statement->u.foreach_s.variable, &temp);

	for (;;) {
		is_done = WJ_call_method(inter, env, statement->line_number,
								iterator.u.object, IS_DONE_METHOD_NAME,
								0, NULL);

		if (is_done.type != WJ_BOOLEAN_VALUE) {
			wj_runtime_error(statement->line_number, NOT_BOOLEAN_TYPE_ERR, MESSAGE_ARGUMENT_END);
		}
		if (is_done.u.boolean_value)
			break;

		*var = WJ_call_method(inter, env, statement->line_number,
							iterator.u.object, CURRENT_ITEM_METHOD_NAME,
							0, NULL);

		result = wj_execute_statement_list(inter, env, statement->u.for_s.block->statement_list);

		if (result.type == RETURN_STATEMENT_RESULT) {
			break;
		} else if (result.type == BREAK_STATEMENT_RESULT) {
			result.type = compare_labels(result.u.label, statement->u.for_s.label, result.type);
			break;
		} else if (result.type == CONTINUE_STATEMENT_RESULT) {
			result.type = compare_labels(result.u.label, statement->u.for_s.label, result.type);
		}

		WJ_call_method(inter, env, statement->line_number, iterator.u.object,
						NEXT_METHOD_NAME, 0, NULL);
	}
	WJ_shrink_stack(inter, stack_count);

	return result;
}


static StatementResult
execute_return_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                         Statement *statement)
{
    StatementResult result;

    result.type = RETURN_STATEMENT_RESULT;
    if (statement->u.return_s.return_value) {
        result.u.return_value
            = wj_eval_expression(inter, env,
                                  statement->u.return_s.return_value);
    } else {
        result.u.return_value.type = WJ_NULL_VALUE;
    }

    return result;
}

static StatementResult
execute_break_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                        Statement *statement)
{
    StatementResult result;

    result.type = BREAK_STATEMENT_RESULT;

    return result;
}

static StatementResult
execute_continue_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                           Statement *statement)
{
    StatementResult result;

    result.type = CONTINUE_STATEMENT_RESULT;

    return result;
}
static StatementResult
execute_throw_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
						Statement *statement)
{
	WJ_Value *ex_val;

	ex_val = wj_eval_and_peek_expression(inter, env, 
										 statement->u.throw_s.exception);

	inter->current_exception = *ex_val;

	WJ_shrink_stack(inter, 1);

	longjmp(inter->current_recovery_environment.environment, LONGJMP_ARG);
}

static StatementResult
execute_statement(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                  Statement *statement)
{
    StatementResult result;

    result.type = NORMAL_STATEMENT_RESULT;

    switch (statement->type) {
    case EXPRESSION_STATEMENT:
        result = execute_expression_statement(inter, env, statement);
        break;
    case GLOBAL_STATEMENT:
        result = execute_global_statement(inter, env, statement);
        break;
    case IF_STATEMENT:
        result = execute_if_statement(inter, env, statement);
        break;
    case WHILE_STATEMENT:
        result = execute_while_statement(inter, env, statement);
        break;
    case FOR_STATEMENT:
        result = execute_for_statement(inter, env, statement);
        break;
	case FOREACH_STATEMENT:
		result = execute_foreach_statement(inter, env, statement);
		break;
    case RETURN_STATEMENT:
        result = execute_return_statement(inter, env, statement);
        break;
    case BREAK_STATEMENT:
        result = execute_break_statement(inter, env, statement);
        break;
    case CONTINUE_STATEMENT:
        result = execute_continue_statement(inter, env, statement);
        break;
	case THROW_STATEMENT:
		result = execute_throw_statement(inter, env, statement);
		break;
    case STATEMENT_TYPE_COUNT_PLUS_1:   /* FALLTHRU */
    default:
        DBG_panic(("bad case...%d", statement->type));
    }

    return result;
}

StatementResult
wj_execute_statement_list(WJ_Interpreter *inter, WJ_LocalEnvironment *env,
                           StatementList *list)
{
    StatementList *pos;
    StatementResult result;

    result.type = NORMAL_STATEMENT_RESULT;
    for (pos = list; pos; pos = pos->next) {
        result = execute_statement(inter, env, pos->statement);
        if (result.type != NORMAL_STATEMENT_RESULT)
            goto FUNC_END;
    }

  FUNC_END:
    return result;
}
